/**************************************************************************************
*
*	Copyright (C) 2010 - 2012 Brent Andrew O. Chua - See LICENSE.txt for more info.
*		
*	Filename	-	BlendState.cpp
*
*	Comments	-	See BlendState.h
*
**************************************************************************************/
#include "../Include/BlendState.h"
#include "../Include/Engine.h"
#include "../Include/GraphicsDevice.h"
#include "../Include/AsyncLoadResource.h"
#include "../Include/XMLParser.h"
#include "../Include/GraphicsTypesMapping.h"

namespace Pulse
{
	//EErrorCode::Type RenderTargetBlendDesc::ParseParameters( XMLElementPtr pElem )
	//{


	//	return EErrorCode::OKAY;
	//}

	BlendStateDesc::BlendStateDesc( void )
	{
		SetDefault();
	}

	void BlendStateDesc::SetDefault( void )
	{
		m_filename.Clear();
		bAlphaToCoverageEnable = FALSE;
		bIndependentBlendEnable = FALSE;

		for ( INT i = 0; i < 8; ++i )
		{
			renderTargets[i].blendEnable			= FALSE;
			renderTargets[i].srcBlend				= EBlendType::ONE;
			renderTargets[i].destBlend				= EBlendType::ZERO;
			renderTargets[i].blendOp				= EBlendOp::ADD;
			renderTargets[i].srcBlendAlpha			= EBlendType::ONE;
			renderTargets[i].destBlendAlpha			= EBlendType::ZERO;
			renderTargets[i].blendOpAlpha			= EBlendOp::ADD;
			renderTargets[i].renderTargetWriteMask	= EColorWriteEnable::ALL;
		}
	}

	void BlendStateDesc::SetToLoad( const CHAR *pFilename )
	{
		m_filename = pFilename;
	}

	BlendState::BlendState( void )
	{

	}

	BlendState::~BlendState( void )
	{

	}


	BOOL BlendState::CreateResource( BaseResDesc *pDesc )	
	{
		if ( pDesc->GetType() != GetType() )
			return FALSE;

		m_desc = *((BlendStateDesc*)pDesc);

		if ( PerformInitialize( m_desc.IsAsyncLoad() ) == EErrorCode::OKAY )
			SetFlagCreated();

		return IsCreated();	
	}

	BOOL BlendState::DestroyResource( void )				
	{
		UnloadResource();
		SetFlagDestroyed();	
		return TRUE;	
	}

	BOOL BlendState::LoadResource( void )				
	{ 
		if ( Initialize() == EErrorCode::OKAY )
		{
			SetFlagLoaded();	
			return TRUE;	
		}

		return FALSE;
	}

	BOOL BlendState::UnloadResource( void )				
	{
		Cleanup();
		SetFlagUnloaded();
		return FALSE;	
	}

	EErrorCode::Type BlendState::Initialize( void )
	{
		ID3D11Device *pDevice = (ID3D11Device*)GraphicsDevice::GetInstance()->GetNakedDevice();

		if ( m_desc.m_filename.IsEmpty() == FALSE )
		{
			XMLDocumentPtr pDoc = XMLParser::Load( (PSX_PATH_BLEND_STATES + m_desc.m_filename).GetBuffer() );

			if ( pDoc.IsNull() )
			{
				PSX_PushError( "Failed to load blend state file." );
				return EErrorCode::FILE;
			}

			XMLElementPtr pElem = pDoc->FindChild( PSX_String("BlendState") );

			if ( pElem.IsValid() )
			{
				EErrorCode::Type retCode = ProcessBlendState( pElem );

				if (  retCode != EErrorCode::OKAY )
				{
					return retCode;
				}
			}
			else
			{
				return EErrorCode::INVALID_FORMAT;
			}
		}

		// Translate desc to D3D11_BLEND_DESC
		D3D11_BLEND_DESC desc;

		desc.AlphaToCoverageEnable = m_desc.bAlphaToCoverageEnable;
		desc.IndependentBlendEnable = m_desc.bIndependentBlendEnable;

		for ( SIZE_T i = 0; i < 8; ++i )
		{
			desc.RenderTarget[i].BlendEnable			= m_desc.renderTargets[i].blendEnable;
			desc.RenderTarget[i].SrcBlend				= DX11Mapping::ToDXBlend( m_desc.renderTargets[i].srcBlend );
			desc.RenderTarget[i].DestBlend				= DX11Mapping::ToDXBlend( m_desc.renderTargets[i].destBlend );
			desc.RenderTarget[i].BlendOp				= DX11Mapping::ToDXBlendOp( m_desc.renderTargets[i].blendOp );
			desc.RenderTarget[i].SrcBlendAlpha			= DX11Mapping::ToDXBlend( m_desc.renderTargets[i].srcBlendAlpha );
			desc.RenderTarget[i].DestBlendAlpha			= DX11Mapping::ToDXBlend( m_desc.renderTargets[i].destBlendAlpha );
			desc.RenderTarget[i].BlendOpAlpha			= DX11Mapping::ToDXBlendOp( m_desc.renderTargets[i].blendOpAlpha );
			desc.RenderTarget[i].RenderTargetWriteMask	= DX11Mapping::ToDXWriteEnable( m_desc.renderTargets[i].renderTargetWriteMask );
		}

		if ( E_OUTOFMEMORY == pDevice->CreateBlendState( &desc, &m_pBlendState ) )
		{
			PSX_PushError( "Graphics card out of memory." );
			return EErrorCode::GRAPHICS;
		}

		return EErrorCode::NONE;
	}

	void BlendState::Cleanup( void )
	{
		PSX_SafeRelease( m_pBlendState );
	}

	EErrorCode::Type BlendState::ProcessBlendState( XMLElementPtr pElem )
	{
		XMLElementPtr pChild; 

		// AlphaToCoverageEnable
		{
			pChild = pElem->FindChild( PSX_String("AlphaToCoverageEnable") );

			if ( pChild.IsNull() )
			{
				PSX_PushError( "No AlphaToCoverageEnable found." );
				return EErrorCode::INVALID_FORMAT;
			}

			m_desc.bAlphaToCoverageEnable = pChild->GetValue().ToBOOL();
		}

		// IndependentBlendEnable
		{
			pChild = pElem->FindChild( PSX_String("IndependentBlendEnable") );

			if ( pChild.IsNull() )
			{
				PSX_PushError( "No IndependentBlendEnable found." );
				return EErrorCode::INVALID_FORMAT;
			}

			m_desc.bIndependentBlendEnable = pChild->GetValue().ToBOOL();
		}

		// Process x elements of render target blend states
		{
			EErrorCode::Type retCode;
			UINT i = 0;
			pChild = pElem->FindChild( PSX_String("RenderTargetBlendStates") );
			XMLElementPtr pElem = pChild->GetFirstChild();

			// Only process up to 8 elements
			while ( pElem.IsValid() && pElem->GetName() == PSX_String("Element")  && i < 8 )
			{
				retCode = ProcessRenderTargetBlendState( pElem, &m_desc.renderTargets[i] );

				if ( retCode != EErrorCode::OKAY )
				{
					return retCode;
				}

				++i;
			}
		}

		return EErrorCode::OKAY;
	}

	EErrorCode::Type BlendState::ProcessRenderTargetBlendState( XMLElementPtr pElem, RenderTargetBlendDesc *pState )
	{
		if ( pElem.IsNull() || pState == PSX_NULL )
		{
			return EErrorCode::INVALID_PARAMETER;
		}

		XMLElementPtr pChild; 

		// BlendEnable
		{
			pChild = pElem->FindChild( PSX_String("BlendEnable") );

			if ( pChild.IsNull() )
			{
				PSX_PushError( "No BlendEnable found." );
				return EErrorCode::INVALID_FORMAT;
			}

			pState->blendEnable = pChild->GetValue().ToBOOL();
		}

		// SourceBlend
		{
			pChild = pElem->FindChild( PSX_String("SourceBlend") );

			if ( pChild.IsNull() )
			{
				PSX_PushError( "No SourceBlend found." );
				return EErrorCode::INVALID_FORMAT;
			}

			pState->srcBlend = GraphicsTypesMapping::ToBlendType( pChild->GetValue().GetBuffer() );
		
			if ( pState->srcBlend == EBlendType::_UNUSED_ )
				pState->srcBlend = EBlendType::ONE;
		}

		// DestBlend
		{
			pChild = pElem->FindChild( PSX_String("DestBlend") );

			if ( pChild.IsNull() )
			{
				PSX_PushError( "No DestBlend found." );
				return EErrorCode::INVALID_FORMAT;
			}

			pState->destBlend = GraphicsTypesMapping::ToBlendType( pChild->GetValue().GetBuffer() );
		
			if ( pState->destBlend == EBlendType::_UNUSED_ )
				pState->destBlend = EBlendType::ZERO;
		}

		// BlendOp
		{
			pChild = pElem->FindChild( PSX_String("BlendOp") );

			if ( pChild.IsNull() )
			{
				PSX_PushError( "No BlendOp found." );
				return EErrorCode::INVALID_FORMAT;
			}

			pState->blendOp = GraphicsTypesMapping::ToBlendOp( pChild->GetValue().GetBuffer() );

			if ( pState->blendOp == EBlendOp::_UNUSED_ )
				pState->blendOp = EBlendOp::ADD;
		}

		// SourceBlendAlpha
		{
			pChild = pElem->FindChild( PSX_String("SourceBlendAlpha") );

			if ( pChild.IsNull() )
			{
				PSX_PushError( "No SourceBlendAlpha found." );
				return EErrorCode::INVALID_FORMAT;
			}

			pState->srcBlendAlpha = GraphicsTypesMapping::ToBlendType( pChild->GetValue().GetBuffer() );
		
			if ( pState->srcBlendAlpha == EBlendType::_UNUSED_ )
				pState->srcBlendAlpha = EBlendType::ONE;
		}

		// DestBlendAlpha
		{
			pChild = pElem->FindChild( PSX_String("DestBlendAlpha") );

			if ( pChild.IsNull() )
			{
				PSX_PushError( "No DestBlendAlpha found." );
				return EErrorCode::INVALID_FORMAT;
			}

			pState->destBlendAlpha = GraphicsTypesMapping::ToBlendType( pChild->GetValue().GetBuffer() );
		
			if ( pState->destBlendAlpha == EBlendType::_UNUSED_ )
				pState->destBlendAlpha = EBlendType::ZERO;
		}

		// BlendOpAlpha
		{
			pChild = pElem->FindChild( PSX_String("BlendOpAlpha") );

			if ( pChild.IsNull() )
			{
				PSX_PushError( "No BlendOpAlpha found." );
				return EErrorCode::INVALID_FORMAT;
			}

			pState->blendOpAlpha = GraphicsTypesMapping::ToBlendOp( pChild->GetValue().GetBuffer() );

			if ( pState->blendOpAlpha == EBlendOp::_UNUSED_ )
				pState->blendOpAlpha = EBlendOp::ADD;
		}

		// RenderTargetWriteMask
		{
			pChild = pElem->FindChild( PSX_String("RenderTargetWriteMask") );

			if ( pChild.IsNull() )
			{
				PSX_PushError( "No RenderTargetWriteMask found." );
				return EErrorCode::INVALID_FORMAT;
			}

			pState->renderTargetWriteMask = GraphicsTypesMapping::ToColorWriteMask( pChild->GetValue().GetBuffer() );
		}

		return EErrorCode::OKAY;
	}

}